﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using Tomahawk.Runtime.Audio;
using Tomahawk.Resources.Audio;
using Microsoft.Xna.Framework.Audio;
using System.Collections;
using Microsoft.Xna.Framework;

namespace Tomahawk.Runtime.Logic
{
    public sealed class XAudioEmitterComponent : XComponent
    {
        #region Serializable Properties
        [XmlElement("AudioBank")]
        public string audioBank = "";

        [XmlElement("Audio3D")]
        public bool audio3D = false;
        #endregion

        #region Properties
        private AudioPackage audioPackage = null;

        [XmlIgnore]
        public AudioBank AudioBank { get { if (audioPackage != null) return audioPackage.AudioBank; return null; } }

        private AudioEmitter audioEmmiter = new AudioEmitter();

        [XmlIgnore]
        public AudioEmitter AudioEmmiter { get { return audioEmmiter; } }

        private List<Cue> cues = new List<Cue>();
        #endregion

        #region Initialization
        public XAudioEmitterComponent()
        {
            UpdateRequired = true;
        }
        #endregion

        #region Audio Playback
        public Cue Play(string resource)
        {
            Cue cue = null;
            string[] tokens = resource.Split(new char[] { '@' });

            if (tokens.Count() != 2)
            {
                throw new Exception("Invalid index");
            }

            if (AudioBank == null)
            {
                throw new Exception("Audio component not initialized");
            }

            cue = AudioBank.GetCue(tokens[0], tokens[1]);

            if (cue == null)
            {
                throw new Exception("Sound cue not found");
            }

            if (audio3D)
            {
                cue.Apply3D(Engine.Instance.AudioManager.AudioListener, AudioEmmiter);
            }

            cue.Play();
            cues.Add(cue);

            return cue;
        }

        #endregion

        #region XObject
        protected override bool checkResourcesReady()
        {
            return base.checkResourcesReady() && audioPackage.IsReady;
        }

        private static bool PredicateRemoveCue(Cue cue)
        {
            if (cue.IsStopping || cue.IsStopped)
            {
                cue.Dispose();
                return true;
            }

            return false;
        }

        protected override void OnUpdate(float elapsedTime)
        {
            base.OnUpdate(elapsedTime);

            cues.RemoveAll(PredicateRemoveCue);

            if (audio3D)
            {
                Vector3 deltaMove = (Owner.SceneComponent.SceneNode.Position - audioEmmiter.Position);

                audioEmmiter.Velocity = elapsedTime > 0.0f ? deltaMove / elapsedTime : Vector3.Zero;
                audioEmmiter.Position = Owner.SceneComponent.SceneNode.Position;
                audioEmmiter.Forward = Owner.SceneComponent.SceneNode.WorldMatrix.Forward;
                audioEmmiter.Up = Owner.SceneComponent.SceneNode.WorldMatrix.Up;
                
                foreach (Cue cue in cues)
                {
                    cue.Apply3D(Engine.Instance.AudioManager.AudioListener, audioEmmiter);
                }
            }
        }

        protected override void OnInitialize()
        {
            base.OnInitialize();
            audioPackage = new AudioPackage(audioBank);
        }

        public override void OnBeforeDestroy() 
        {
            base.OnBeforeDestroy();

            foreach (Cue cue in cues)
            {
                cue.Dispose();
            }
        }
        #endregion
    }
}
